package com.hww.common.util;

import android.content.Context;
import android.os.Environment;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.text.format.Formatter;

import com.hww.common.MyAppThis;

import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * desc  : utils about sdcard
 */
public final class SDCardUtils {

    private SDCardUtils() {
        throw new UnsupportedOperationException("u can't instantiate me...");
    }

    /**
     * 判断SD卡是否被环境启用。
     *
     * @return 如果SD卡被启用返回{@code true}，否则返回{@code false}
     */
    public static boolean isSDCardEnableByEnvironment() {
        /*检查外部存储的状态是否为已挂载*/
        return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
    }

    /**
     * 返回由环境决定的SD卡路径。
     *
     * @return 由环境决定的SD卡路径，如果SD卡未启用则返回空字符串
     */
    public static String getSDCardPathByEnvironment() {
        if (isSDCardEnableByEnvironment()) {
            return Environment.getExternalStorageDirectory().getAbsolutePath();
        }
        return "";
    }


    /**
     * 内部存储路径，应用程序私有文件目录，路径为 /data/data/包名/files  或 /data/user/0/包名/files。
     */
    public static String getFilesDir() {
        return MyAppThis.getThis().getFilesDir().getAbsolutePath();
    }

    /**
     * 内部存储路径，应用程序私有缓存目录，默认路径为/data/data/包名/cache  或  /data/user/0/包名/cache 。
     */
    public static String getCacheDir() {
        return MyAppThis.getThis().getCacheDir().getAbsolutePath();
    }

    /**
     * 外部存储路径，应用程序私有文件目录，默认路径为 /storage/emulated/0/Android/data/包名/files。
     */
    public static String getExternalFilesDir() {
        return MyAppThis.getThis().getExternalFilesDir(null).getAbsolutePath();
    }

    /**
     * 外部存储路径，应用程序私有缓存目录，默认路径为/storage/emulated/0/Android/data/包名/cache
     */
    public static String getExternalCacheDir() {
        return MyAppThis.getThis().getExternalCacheDir().getAbsolutePath();
    }

    /**
     * 获取外部存储路径，默认路径为  /storage/emulated/0  。
     * /storage/emulated/0  是一个虚拟路径，它指向外部存储的根目录。在大多数情况下，路径指向的是设备上的内置存储
     * /sdcard/  是标准路径，用于访问外部存储。用于在较老的android版本与设备中有效
     * <p>
     * 从 Android 10 开始，外部存储的访问方式发生了变化。getExternalStorageDirectory() 方法被标记为弃用，不建议继续使用。
     * 取而代之的是使用 {@link Context#getExternalFilesDir(String)}、
     * {@link Context#getExternalCacheDir()}等方法来获取应用程序在共享外部存储空间中的私有目录
     */
    public static String getExternalStorageDirectory() {
        return Environment.getExternalStorageDirectory().getAbsolutePath();
    }

    /**
     * 获取应用程序外部存储目录的绝对路径。默认路径为  /storage/emulated/0/Android/data/包名/files
     *
     * @param type 目录的类型，可以是环境变量中定义的任何类型，如
     *             {@link android.os.Environment#DIRECTORY_MUSIC},
     *             {@link android.os.Environment#DIRECTORY_PODCASTS},
     *             {@link android.os.Environment#DIRECTORY_RINGTONES},
     *             {@link android.os.Environment#DIRECTORY_ALARMS},
     *             {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
     *             {@link android.os.Environment#DIRECTORY_PICTURES},
     *             {@link android.os.Environment#DIRECTORY_MOVIES}.
     *             如果为null，则返回默认的外部存储目录。
     * @return 返回指定类型目录的绝对路径。如果无法获取目录路径，则返回null。
     */
    public static String getExternalFilesDir(String type) {
        /*获取指定类型的外部存储目录，然后获取该目录的绝对路径*/
        return MyAppThis.getThis().getExternalFilesDir(type).getAbsolutePath();
    }

    /**
     * 获取SD卡信息列表。
     * 通过反射获取存储卷的信息，包括路径、状态和是否可移除。
     * 对于Android N及以上版本和以下版本使用不同的方法获取信息。
     *
     * @return SD卡信息列表，如果无法获取信息，则返回空列表。
     */
    public static List<SDCardInfo> getSDCardInfo() {
        List<SDCardInfo> paths = new ArrayList<>();
        StorageManager sm = (StorageManager) MyAppThis.getThis().getSystemService(Context.STORAGE_SERVICE);
        if (sm == null) return paths;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
            List<StorageVolume> storageVolumes = sm.getStorageVolumes();
            try {
                //noinspection JavaReflectionMemberAccess
                Method getPathMethod = StorageVolume.class.getMethod("getPath");
                for (StorageVolume storageVolume : storageVolumes) {
                    boolean isRemovable = storageVolume.isRemovable();
                    String state = storageVolume.getState();
                    String path = (String) getPathMethod.invoke(storageVolume);
                    paths.add(new SDCardInfo(path, state, isRemovable));
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        } else {
            try {
                Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
                //noinspection JavaReflectionMemberAccess
                Method getPathMethod = storageVolumeClazz.getMethod("getPath");
                Method isRemovableMethod = storageVolumeClazz.getMethod("isRemovable");
                //noinspection JavaReflectionMemberAccess
                Method getVolumeStateMethod = StorageManager.class.getMethod("getVolumeState", String.class);
                //noinspection JavaReflectionMemberAccess
                Method getVolumeListMethod = StorageManager.class.getMethod("getVolumeList");
                Object result = getVolumeListMethod.invoke(sm);
                final int length = Array.getLength(result);
                for (int i = 0; i < length; i++) {
                    Object storageVolumeElement = Array.get(result, i);
                    String path = (String) getPathMethod.invoke(storageVolumeElement);
                    boolean isRemovable = (Boolean) isRemovableMethod.invoke(storageVolumeElement);
                    String state = (String) getVolumeStateMethod.invoke(sm, path);
                    paths.add(new SDCardInfo(path, state, isRemovable));
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return paths;
    }

    /**
     * Return the ptah of mounted sdcard.
     *
     * @return the ptah of mounted sdcard.
     */
    public static List<String> getMountedSDCardPath() {
        List<String> path = new ArrayList<>();
        List<SDCardInfo> sdCardInfo = getSDCardInfo();
        if (sdCardInfo == null || sdCardInfo.isEmpty()) return path;
        for (SDCardInfo cardInfo : sdCardInfo) {
            String state = cardInfo.state;
            if (state == null) continue;
            if ("mounted".equals(state.toLowerCase())) {
                path.add(cardInfo.path);
            }
        }
        return path;
    }


    /**
     * 获取外部存储设备的总容量。
     * <p>
     * 本方法通过调用FileUtils的getFsTotalSize方法，传入外部存储设备的路径，
     * 来获取该存储设备的总容量。外部存储设备通常指的是SD卡或者手机内置的存储空间。
     *
     * @return 长整型数值，表示外部存储设备的总容量，单位为字节。
     */
    public static long getExternalTotalSize() {
        return FileUtils.getFsTotalSize(getSDCardPathByEnvironment());
    }

    /**
     * 获取外部存储设备的可用容量大小。
     * <p>
     * 本方法通过调用FileUtils的getFsAvailableSize方法，结合获取的外部存储设备路径，
     * 来获取该设备上可用的存储空间大小。外部存储设备通常指的是SD卡或者USB存储设备，
     * 这里主要是为了获取用户可以自由使用的存储空间，而非系统或应用专用空间。
     *
     * @return 长整型数值，表示外部存储设备的可用容量，单位为字节。
     */
    public static long getExternalAvailableSize() {
        return FileUtils.getFsAvailableSize(getSDCardPathByEnvironment());
    }

    /**
     * 获取设备内部存储的总容量。
     * <p>
     * 本方法通过调用FileUtils的getFsTotalSize方法，传入内部存储路径，来获取内部存储的总容量。
     * 内部存储通常指设备上由操作系统管理的存储区域，用于存储应用程序及其数据。
     *
     * @return 长整型数字，表示内部存储的总容量，单位为字节。
     */
    public static long getInternalTotalSize() {
        return FileUtils.getFsTotalSize(Environment.getDataDirectory().getAbsolutePath());
    }


    /**
     * 获取内部存储空间的可用大小。
     * <p>
     * 本方法通过调用FileUtils的getFsAvailableSize方法，传入系统数据目录的绝对路径，
     * 来获取内部存储空间中可用的字节大小。这有助于应用程序了解内部存储空间的剩余情况，
     * 以便在需要存储数据时做出相应的决策。
     *
     * @return 长整型数值，表示内部存储空间的可用字节大小。
     */
    public static long getInternalAvailableSize() {
        return FileUtils.getFsAvailableSize(Environment.getDataDirectory().getAbsolutePath());
    }

    /*定义一个公共的静态类SDCardInfo，用于封装SD卡的信息*/
    public static class SDCardInfo {
        /*SD卡路径*/
        private String path;
        /*SD卡状态*/
        private String state;
        /*是否可以移除*/
        private boolean isRemovable;
        /*总大小(字节)*/
        private long totalSize;
        /*可用大小(字节)*/
        private long availableSize;

        SDCardInfo(String path, String state, boolean isRemovable) {
            this.path = path;
            this.state = state;
            this.isRemovable = isRemovable;
            this.totalSize = FileUtils.getFsTotalSize(path);
            this.availableSize = FileUtils.getFsAvailableSize(path);
        }

        public String getPath() {
            return path;
        }

        public String getState() {
            return state;
        }

        public boolean isRemovable() {
            return isRemovable;
        }

        public long getTotalSize() {
            return totalSize;
        }

        public long getAvailableSize() {
            return availableSize;
        }

        @Override
        public String toString() {
            return "SDCardInfo {" +
                    "path = " + path +
                    ", state = " + state +
                    ", isRemovable = " + isRemovable +
                    ", totalSize = " + Formatter.formatFileSize(MyAppThis.getThis(), totalSize) +
                    ", availableSize = " + Formatter.formatFileSize(MyAppThis.getThis(), availableSize) +
                    '}';
        }
    }
}
